#include "NBRenderEngineDX12.h"




namespace EngineRender {

	NBRenderEngineDX12::NBRenderEngineDX12()
	{
	}


	NBRenderEngineDX12::~NBRenderEngineDX12()
	{
	}

	_hr NBRenderEngineDX12::SetUp(EngineCore::EngineProperty & prop, PlatformLayer & platform)
	{
		m_swapBufferCount = prop.BufferCount;


		UINT dxgiFactoryFlags = 0;// DXGI_CREATE_FACTORY_DEBUG;//Debug Value 
		ComPtr<IDXGIFactory4> factory;
		CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory));

		//Enumerate adapters info 
		//TODO: Maybe Need to Get Output Info
		UINT adapterOrder = 0;
		IDXGIAdapter1* adapter;
		ComPtr<IDXGIAdapter1> useAdapter;
		while (factory->EnumAdapters1(adapterOrder,&adapter) != DXGI_ERROR_NOT_FOUND)
		{
			DXGI_ADAPTER_DESC1 desc;
			adapter->GetDesc1(&desc);
			m_adapterList.push_back(std::move(std::wstring(desc.Description)));

			// Maybe Need to Get Output Info

			/*ComPtr<IDXGIOutput> output;
			adapter->EnumOutputs(0, &output);
			DXGI_OUTPUT_DESC outputDesc;
			output->GetDesc(&outputDesc);
			outputDesc.DeviceName;
			outputDesc.AttachedToDesktop;
			UINT count;
			output->GetDisplayModeList1(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &count, nullptr);
			std::vector<DXGI_MODE_DESC1> modeList(count);
			output->GetDisplayModeList1(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &count, &modeList[0]);*/

			//End 
			++adapterOrder;
			if (m_device) {
				continue;
				adapter->Release();
			}

			//Test Adapter Support DX12
			if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
			{
				if (FAILED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)))) {
					//TODO:Fix the Error;

				}
			}
			adapter->Release();
		}

		//Create Common Queue
		//TODO: Multi Queue Different with Priority 
		D3D12_COMMAND_QUEUE_DESC queueDesc = {};
		queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
		queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
		queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;

		m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_cmdQueue_high_direct));

		
		//Create Swap chain
		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
		swapChainDesc.BufferCount = prop.BufferCount;
		swapChainDesc.Width = prop.Width;
		swapChainDesc.Height = prop.Height;
		swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
		//TODO :  TO Use MSAA OR Other AntiAlias Tech
		swapChainDesc.SampleDesc.Count = 1;
		swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;

		ComPtr<IDXGISwapChain1> swapChain;
		factory->CreateSwapChainForHwnd(
			m_cmdQueue_high_direct.Get(),
			platform.GetHwnd(),
			&swapChainDesc,
			nullptr,
			nullptr,
			&swapChain
			);
		swapChain.As(&m_swapChain);
		m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();

		m_heapManager.SetUp(m_device.Get(), prop.RTVDescCount, prop.DSTDescCount, prop.CSVDescCount);
		m_renderTarget.resize(prop.BufferCount,nullptr);

		int startPos = m_heapManager.AlloctDescHandle(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, prop.BufferCount,nullptr,nullptr);
		for (uint32_t i = 0; i < prop.BufferCount; i++)
		{
			m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_renderTarget[i]));
			m_device->CreateRenderTargetView(m_renderTarget[i].Get(), nullptr, m_heapManager.GetDescHandleCPU(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, startPos));
			++startPos;
		}

		//Tempt
		HRESULT hr;
		hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_cmdAlloc));
		hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_cmdAlloc.Get(), nullptr, IID_PPV_ARGS(&m_cmdList));
		hr = m_device->CreateFence(0,D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
		m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
		hr = m_cmdList->Close();

		m_viewPort.TopLeftX = 0;
		m_viewPort.TopLeftY = 0;
		m_viewPort.Width = static_cast<FLOAT>(prop.Width);
		m_viewPort.Height = static_cast<FLOAT>(prop.Height);
		m_viewPort.MinDepth = 0.0f;
		m_viewPort.MaxDepth = 1.0f;

		m_scissorRect.top = 0;
		m_scissorRect.left = 0;
		m_scissorRect.right = static_cast<LONG>(prop.Width);
		m_scissorRect.bottom = static_cast<LONG>(prop.Height);

		//end

		return _OK;
	}

	_hr NBRenderEngineDX12::Launch()
	{
		return _OK;
	}
	_hr NBRenderEngineDX12::Render()
	{
		// Tempt Render Test 
		HRESULT hr;
		hr = m_cmdAlloc->Reset();
		hr = m_cmdList->Reset(m_cmdAlloc.Get(), nullptr);

		m_cmdList->RSSetViewports(1, &m_viewPort);
		m_cmdList->RSSetScissorRects(1, &m_scissorRect);

		D3D12_RESOURCE_BARRIER barrier = {};
		barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
		barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
		barrier.Transition.pResource = m_renderTarget[m_frameIndex].Get();
		barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
		barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
		barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;

		m_cmdList->ResourceBarrier(1, &barrier);

		m_cmdList->OMSetRenderTargets(1, &m_heapManager.GetDescHandleCPU(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, m_frameIndex), FALSE, nullptr);
		const float Clearcolor[] = { 0.0f,0.3f,0.2f,1.0f };
		m_cmdList->ClearRenderTargetView(m_heapManager.GetDescHandleCPU(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, m_frameIndex), Clearcolor, 0, nullptr);

		barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
		barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
		m_cmdList->ResourceBarrier(1, &barrier);

		hr = m_cmdList->Close();
		ID3D12CommandList* cmdList[] = { m_cmdList.Get() };
		m_cmdQueue_high_direct->ExecuteCommandLists(1,cmdList);

		hr = m_swapChain->Present(1, 0);
		m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();

		hr = m_cmdQueue_high_direct->Signal(m_fence.Get(), m_fenceNum);
		hr = m_fence->SetEventOnCompletion( m_fenceNum,m_fenceEvent);
		WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
		m_fenceNum++;
		//end
		return _OK;
	}
}